/* 
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

using System;
using Lucene.Net.Support;
using IndexInput = Lucene.Net.Store.IndexInput;
using UnicodeUtil = Lucene.Net.Util.UnicodeUtil;

namespace Lucene.Net.Index
{
    
    sealed class TermBuffer : System.ICloneable
    {
        
        private System.String field;
        private Term term; // cached
        private bool preUTF8Strings; // true if strings are stored in modified UTF8 encoding (LUCENE-510)
        private bool dirty; // true if text was set externally (ie not read via UTF8 bytes)
        
        private UnicodeUtil.UTF16Result text = new UnicodeUtil.UTF16Result();
        private UnicodeUtil.UTF8Result bytes = new UnicodeUtil.UTF8Result();
        
        public int CompareTo(TermBuffer other)
        {
            if ((System.Object) field == (System.Object) other.field)
            // fields are interned
                return CompareChars(text.result, text.length, other.text.result, other.text.length);
            else
                return String.CompareOrdinal(field, other.field);
        }
        
        private static int CompareChars(char[] chars1, int len1, char[] chars2, int len2)
        {
            int end = len1 < len2?len1:len2;
            for (int k = 0; k < end; k++)
            {
                char c1 = chars1[k];
                char c2 = chars2[k];
                if (c1 != c2)
                {
                    return c1 - c2;
                }
            }
            return len1 - len2;
        }
        
        /// <summary>Call this if the IndexInput passed to <see cref="Read" />
        /// stores terms in the "modified UTF8" (pre LUCENE-510)
        /// format. 
        /// </summary>
        internal void  SetPreUTF8Strings()
        {
            preUTF8Strings = true;
        }
        
        public void  Read(IndexInput input, FieldInfos fieldInfos)
        {
            this.term = null; // invalidate cache
            int start = input.ReadVInt();
            int length = input.ReadVInt();
            int totalLength = start + length;
            if (preUTF8Strings)
            {
                text.SetLength(totalLength);
                input.ReadChars(text.result, start, length);
            }
            else
            {
                
                if (dirty)
                {
                    // Fully convert all bytes since bytes is dirty
                    UnicodeUtil.UTF16toUTF8(text.result, 0, text.length, bytes);
                    bytes.SetLength(totalLength);
                    input.ReadBytes(bytes.result, start, length);
                    UnicodeUtil.UTF8toUTF16(bytes.result, 0, totalLength, text);
                    dirty = false;
                }
                else
                {
                    // Incrementally convert only the UTF8 bytes that are new:
                    bytes.SetLength(totalLength);
                    input.ReadBytes(bytes.result, start, length);
                    UnicodeUtil.UTF8toUTF16(bytes.result, start, length, text);
                }
            }
            this.field = fieldInfos.FieldName(input.ReadVInt());
        }
        
        public void  Set(Term term)
        {
            if (term == null)
            {
                Reset();
                return ;
            }
            System.String termText = term.Text;
            int termLen = termText.Length;
            text.SetLength(termLen);
            TextSupport.GetCharsFromString(termText, 0, termLen, text.result, 0);
            dirty = true;
            field = term.Field;
            this.term = term;
        }
        
        public void  Set(TermBuffer other)
        {
            text.CopyText(other.text);
            dirty = true;
            field = other.field;
            term = other.term;
        }
        
        public void  Reset()
        {
            field = null;
            text.SetLength(0);
            term = null;
            dirty = true;
        }
        
        public Term ToTerm()
        {
            if (field == null)
            // unset
                return null;
            
            if (term == null)
                term = new Term(field, new System.String(text.result, 0, text.length), false);
            
            return term;
        }
        
        public System.Object Clone()
        {
            TermBuffer clone = null;
            try
            {
                clone = (TermBuffer) base.MemberwiseClone();
            }
            catch (System.Exception)
            {
            }
            
            clone.dirty = true;
            clone.bytes = new UnicodeUtil.UTF8Result();
            clone.text = new UnicodeUtil.UTF16Result();
            clone.text.CopyText(text);
            return clone;
        }
    }
}